//
//  NSData+TDES.m
//  CLExtension
//
//  Created by CharlieLiao on 2017/8/2.
//  Copyright © 2017年 Charlie. All rights reserved.
//

#import "CLDES.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (TripleDES)


#pragma mark - 三重DES加密与解密

/// 三重DES加密
- (NSData*)encryptTDESWithKey:(NSString*)key
{
    return [self encryptTDESWithKey:key andIV:nil padding:SecurityPaddingZero mode:SecurityModeECB];
}

/// 三重DES解密
- (NSData*)decryptTDESWithKey:(NSString*)key
{
    return [self decryptTDESWithKey:key andIV:nil padding:SecurityPaddingZero mode:SecurityModeECB];
}

/// 三重DES加密
- (NSData*)encryptTDESWithKey:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    return [self tripleDES:kCCEncrypt key:key andIV:iv padding:padding mode:mode];
}

/// 三重DES解密
- (NSData*)decryptTDESWithKey:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    return [self tripleDES:kCCDecrypt key:key andIV:iv padding:padding mode:mode];
}

/// 三重DES加密与解密
- (NSData*)tripleDES:(CCOperation)operation key:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    // 算法模式
    CCOptions options;
    switch (mode) {
        case SecurityModeCBC: // Default is CBC.
            options = 0;
            break;
        case SecurityModeECB:
            options = kCCOptionECBMode;
            break;
    }
    
    //
    size_t rawSize = self.length;
    size_t dataSize = rawSize;
    
    // 填充模式
    switch (padding) {
        case SecurityPaddingZero: {  // 需要填充0
            uint remainder = rawSize % kCCBlockSize3DES;
            if (remainder > 0)
                dataSize += kCCBlockSize3DES - remainder;
        }
            break;
            
        case SecurityPaddingPKCS7:
            options |= kCCOptionPKCS7Padding;
            break;
    }
    
    //
    char *dataPtr = malloc(dataSize);
    memset(dataPtr, 0x0, dataSize);
    memcpy(dataPtr, self.bytes, rawSize);
//    char dataPtr[dataSize];
//    memcpy(dataPtr, self.bytes, rawSize);
//    memset(&dataPtr[rawSize], 0, dataSize-rawSize);
    
    size_t bufferPtrSize = (dataSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
//    uint8_t bufferPtr[bufferPtrSize];
    char *bufferPtr = malloc(bufferPtrSize);
    size_t movedBytes = 0;
    
    CCCryptorStatus ccStatus = CCCrypt(operation,
                                       kCCAlgorithm3DES,
                                       options,
                                       key.UTF8String,   //key
                                       kCCKeySize3DES,
                                       iv.UTF8String,    //iv
                                       dataPtr,
                                       dataSize,
                                       bufferPtr,
                                       bufferPtrSize,
                                       &movedBytes);
    
    free(dataPtr);
    
    if (ccStatus == kCCSuccess)
        return [NSData dataWithBytesNoCopy:bufferPtr length:movedBytes freeWhenDone:true];
//        return [NSData dataWithBytes:bufferPtr length:movedBytes];
    
    return nil;
}

//if (ccStatus == kCCSuccess) NSLog(@"SUCCESS");
/*else if (ccStatus == kCCParamError) return @"PARAM ERROR";
 else if (ccStatus == kCCBufferTooSmall) return @"BUFFER TOO SMALL";
 else if (ccStatus == kCCMemoryFailure) return @"MEMORY FAILURE";
 else if (ccStatus == kCCAlignmentError) return @"ALIGNMENT";
 else if (ccStatus == kCCDecodeError) return @"DECODE ERROR";
 else if (ccStatus == kCCUnimplemented) return @"UNIMPLEMENTED"; */

@end


@implementation NSData (CLDES)

/// DES加密
- (NSData*)encryptDESWithKey:(NSString*)key
{
    return [self encryptDESWithKey:key andIV:nil padding:SecurityPaddingPKCS7 mode:SecurityModeECB];
}

/// DES解密
- (NSData*)decryptDESWithKey:(NSString*)key
{
    return [self decryptDESWithKey:key andIV:nil padding:SecurityPaddingPKCS7 mode:SecurityModeECB];
}

/// DES加密
- (NSData*)encryptDESWithKey:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    return [self DES:kCCEncrypt key:key andIV:iv padding:padding mode:mode];
}

/// DES解密
- (NSData*)decryptDESWithKey:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    return [self DES:kCCDecrypt key:key andIV:iv padding:padding mode:mode];
}

/// DES加密与解密
- (NSData*)DES:(CCOperation)operation key:(NSString*)key andIV:(NSString *)iv padding:(SecurityPadding)padding mode:(SecurityMode)mode
{
    // 算法模式
    CCOptions options;
    switch (mode) {
        case SecurityModeCBC: // Default is CBC.
            options = 0;
            break;
        case SecurityModeECB:
            options = kCCOptionECBMode;
            break;
    }
    
    //
    size_t rawSize = self.length;
    size_t dataSize = rawSize;
    
    // 填充模式
    switch (padding) {
        case SecurityPaddingZero: {  // 需要填充0
            uint remainder = rawSize % kCCBlockSizeDES;
            if (remainder > 0)
                dataSize += kCCBlockSizeDES - remainder;
        }
            break;
            
        case SecurityPaddingPKCS7:
            options |= kCCOptionPKCS7Padding;
            break;
    }
    
    //
    char dataPtr[dataSize];
    memcpy(dataPtr, self.bytes, rawSize);
    memset(&dataPtr[rawSize], 0, dataSize-rawSize);
    
    size_t bufferPtrSize = (dataSize + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
    uint8_t bufferPtr[bufferPtrSize];
    
    size_t movedBytes = 0;
    
    CCCryptorStatus ccStatus = CCCrypt(operation,
                                       kCCAlgorithmDES,
                                       options,
                                       key.UTF8String,   //key
                                       kCCKeySizeDES,
                                       iv.UTF8String,    //iv
                                       dataPtr,
                                       dataSize,
                                       bufferPtr,
                                       bufferPtrSize,
                                       &movedBytes);
    
    if (ccStatus == kCCSuccess)
        return [NSData dataWithBytes:bufferPtr length:movedBytes];
    
    return nil;
}

@end
